#include "PageCache.h"

PageCache PageCache::_sInstance;

// 实现PageCache的构造函数
PageCache::PageCache()
{
    // 初始化工作
}

Span *PageCache::NewSpan(size_t k)
{
    assert(k > 0 && k < NPAGES);
    if (!_spanLists[k].Empty())
    {
        return _spanLists[k].PopFront();
    }
    else // 说明第k个位置是空的
    {
        // 检查一下后面的桶里有没有span，如果有可以把他进行切分
        for (size_t i = k + 1; i < NPAGES; ++i)
        {
            if (!_spanLists[i].Empty())
            {
                Span *nSpan = _spanLists[i].PopFront();
                Span *kSpan = _spanPool.New();

                // 在nSpan的头部切一个k页下来
                kSpan->_pageID = nSpan->_pageID;
                kSpan->_n = k;

                nSpan->_pageID += k;
                nSpan->_n -= k;

                
                _spanLists[nSpan->_n].PushFront(nSpan);
                //存储nspan的首尾页号和span的映射
                _idSpanMap[nSpan->_pageID] = nSpan;
                _idSpanMap[nSpan->_pageID + nSpan->_n - 1] = nSpan;

                // 建立PAGE_ID和Span的映射，方便回收时，能根据pageid找到对应的span
                for (PAGE_ID i = 0; i < kSpan->_n; ++i)
                {
                    _idSpanMap[kSpan->_pageID + i] = kSpan;
                }

                return kSpan;
            }
        }
        // 走到这个位置就说明后面没有大页的Span
        // 这时就去找堆要一个128页的span
        Span *bigSpan = _spanPool.New();
        void *ptr = SystemAlloc(NPAGES - 1);
        bigSpan->_pageID = (PAGE_ID)ptr >> PAGE_SHIFT;
        bigSpan->_n = NPAGES - 1;
        _spanLists[bigSpan->_n].PushFront(bigSpan);
        return NewSpan(k);
    }
}

Span *PageCache::MapObjectToSpan(void *obj)
{
    PAGE_ID id = (PAGE_ID)obj >> PAGE_SHIFT;
    auto ret = _idSpanMap.find(id);
    if (ret != _idSpanMap.end())
    {
        return ret->second;
    }
    else
    {
        assert(false);
        return nullptr;
    }
}

//把从CentralCache还回来的内存进行合并的函数
void PageCache::ReleaseSpanToPageCache(Span *span)
{
    //死循环，直到不能合并为止
    while(1)
    {
        //记录当前页ID的前一页ID
        PAGE_ID prevID = span->_pageID - 1;
        //从map中根据页号查找前一页的span
        auto ret = _idSpanMap.find(prevID);
        //前面的页号没有，不进行合并
        if(ret == _idSpanMap.end())
        {
            //跳出循环
            break;
        }
        //前面相邻页的span在使用，不合并
        Span* prevSpan = ret->second;
        if(ret->second->_isUse == true)
        {
            break;
        }
        //合并超出128页的span没办法管理，也没法合并，就退出
        if(prevSpan->_n + span->_n > 128)
        {
            break;
        }
        //把当前span的页号设置为前一页的页号
        span->_pageID = prevSpan->_pageID;
        //把当前span中的页的数量加上前一个span的数量
        span->_n += prevSpan->_n;
        //prevSpan已经被合并了，不需要了
        _spanLists[prevSpan->_n].Erase(prevSpan);
        //把当前prevSpan删除
        // delete prevSpan;
        _spanPool.Delete(prevSpan);
    }

    //向后合并
    while(1)
    {
        //记录当前页ID的后一页ID
        PAGE_ID nextID = span->_pageID + span->_n;
        //从map中根据页号查找后一页的span
        auto ret = _idSpanMap.find(nextID);
        //后面的页号没有，不进行合并
        if(ret == _idSpanMap.end())
        {
            break;
        }
        //后面相邻页的span在使用，不合并
        Span* nextSpan = ret->second;
        if(ret->second->_isUse == true)
        {
            break;
        }
        //合并超出128页的span没办法管理，也没法合并，就退出
        if(span->_n + nextSpan->_n > 128)
        {
            break;
        }
        //合并
        span->_n += nextSpan->_n;
        _spanLists[nextSpan->_n].Erase(nextSpan);
        _spanPool.Delete(nextSpan);
    }
    //把合并后的span插入到对应的桶里
    _spanLists[span->_n].PushFront(span);
    //把合并后的span设置为未使用
    span->_isUse = false;
    //把合并后的span的首页号和span的映射插入到map中
    _idSpanMap[span->_pageID] = span;
    //把合并后的span的尾页号和span的映射插入到map中
    _idSpanMap[span->_pageID + span->_n - 1] = span;
}
